package application;

import java.util.ArrayList;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

public class MapElement {
	//Static identifiers
	public static int STATION = 0;
	public static int TRAIN = 1;
	public static int TRACK = 2;
	public static int TRACKCONTROLLER = 3;
	public static int MBOSEGMENT = 4;
	public static int YARD = 5;
	public static int HIGHLIGHT = 6;
	public static int HIGHLIGHT_WIDTH = 3;

	//Object variables
	private int type;
	private int parentID;
	private Object data;
	private ArrayList<Double> X;
	private ArrayList<Double> Y;
	private double centerX, centerY;
	private double offset;
	private double slope;
	
	public static String name[] = {"Station", "Train", "Track", "Track Controller", "MBO Segment", "Yard", "", "", "", ""};
	public static Color color[] = {Color.rgb(0, 102, 204), Color.rgb(204, 0, 0), Color.BLACK, Color.rgb(255, 102, 0), Color.rgb(204, 0, 0), Color.rgb(204, 204, 204), Color.rgb(0, 204, 102)};

	public MapElement() {
		X = new ArrayList<Double>();
		Y = new ArrayList<Double>();
	}
	public MapElement(int pid, int t, double x, double y) {
		this();

		this.type = t;
		this.parentID = pid;
		this.slope = 0;

		if(this.type == MapElement.STATION || this.type == MapElement.TRAIN || this.type == MapElement.TRACKCONTROLLER || this.type == (MapElement.STATION + MapElement.HIGHLIGHT) || this.type == (MapElement.TRAIN + MapElement.HIGHLIGHT) || this.type == (MapElement.TRACKCONTROLLER + MapElement.HIGHLIGHT))
			makeCircle(x, y);
		else if(this.type == MapElement.TRACK || this.type == (MapElement.TRACK + MapElement.HIGHLIGHT))
			makeLine(x, y, true);
		else if(this.type == MapElement.MBOSEGMENT)
			makeLine(x, y, false);
		else if(this.type == MapElement.YARD)
			makeRectangle(x, y);
	}

	public MapElement(int pid, int t, double[] x, double[] y) {
		this();

		this.type = t;
		this.parentID = pid;
		this.offset = 2;

		for(int i = 0; i < x.length; i++) {
			this.X.add(x[i]);
			this.Y.add(y[i]);
		}

		this.slope = (y[0] - y[y.length - 1])/(x[0] - x[x.length - 1]);
	}

	public MapElement(MapElement m) {
		this(m.getParentID(), m.getType(), m.getCenterX(), m.getCenterY());
		this.X = m.getX();
		this.Y = m.getY();
		this.slope = m.getSlope();
	}

	public ArrayList<Double> getX() { return this.X; }
	public ArrayList<Double> getY() { return this.Y; }
	public double getCenterX() { return this.centerX; }
	public double getCenterY() { return this.centerY; }
	public double getOffset() { return this.offset; }
	public double getSlope() { return this.slope; }
	public int getType() { return this.type; }
	public static String getName(int t) { return MapElement.name[t]; }
	public int getParentID() { return this.parentID; }
	public void setParentID(int pid) { this.parentID = pid; }
	public void setData(Object o) { this.data = o; }
	public Object getData() { return this.data; }
	public void highlight() { this.type += MapElement.HIGHLIGHT; }

	//Creating
	public void makeCircle(double x, double y) {
		this.offset = 7.5;
		this.X.add(x - this.offset);
		this.Y.add(y - this.offset);
		this.centerX = x;
		this.centerY = y;
	}

	public void makeLine(double x, double y, boolean fill) {
		this.offset = 2.5;
	}

	public void makeRectangle(double x, double y) {
		this.offset = 7.5;
		this.X.add(x);
		this.Y.add(y);
		this.centerX = x + 4*this.offset;
		this.centerY = y + 3*this.offset;
	}

	//Updating
	public void updatePosition(double x, double y) {
		this.X.clear();
		this.Y.clear();
		
		this.X.add(x);
		this.Y.add(y);
	}

	public void updateNodes(double x1, double y1, double x2, double y2) {
		this.X.set(0, x1);
		this.Y.set(0, y1);
		this.X.set(1, x2);
		this.Y.set(1, y2);
	}

	//Drawing
	public void drawElement(GraphicsContext g) {
		//Checks for highlights
		if(this.type >= MapElement.HIGHLIGHT)
			drawElementHighlight(g);

		if(this.type == MapElement.STATION)
			drawStation(g);
		else if(this.type == MapElement.TRAIN)
			drawTrain(g);
		else if(this.type == MapElement.TRACK)
			drawTrack(g);
		else if(this.type == MapElement.TRACKCONTROLLER)
			drawTrackController(g);
		//else if(this.type == MapElement.MBOSEGMENT)
			//drawMBO(g);
		else if(this.type == MapElement.YARD)
			drawYard(g);
	}

	private void drawYard(GraphicsContext g) {
		g.setFill(MapElement.color[MapElement.YARD]);
		g.fillRoundRect(this.X.get(0), this.Y.get(0), 8*this.offset, 6*this.offset, 10, 10);
		g.setFill(Color.BLACK);
		g.fillText("YARD", this.X.get(0) + 2*this.offset, this.Y.get(0) + 3.5*this.offset);
	}

	private void drawStation(GraphicsContext g) {
		g.setFill(MapElement.color[MapElement.STATION]);
		g.fillOval(this.X.get(0), this.Y.get(0), 2*this.offset, 2*this.offset);
	}

	private void drawTrain(GraphicsContext g) {
		g.setFill(MapElement.color[MapElement.TRAIN]);
		g.fillOval(this.X.get(0), this.Y.get(0), 2*this.offset, 2*this.offset);
	}

	public void drawTrack(GraphicsContext g) {
		g.setStroke(MapElement.color[MapElement.TRACK]);
        g.setLineWidth(2*this.offset);
        
        for(int i = 0; i < (this.X.size() -1) && i < (this.Y.size() - 1); i++)
        	g.strokeLine(this.X.get(i), this.Y.get(i), this.X.get(i + 1), this.Y.get(i + 1));
	}

	private void drawTrackController(GraphicsContext g) {
		g.setFill(MapElement.color[MapElement.TRACKCONTROLLER]);
		g.fillOval(this.X.get(0), this.Y.get(0), 2*this.offset, 2*this.offset);
	}

	//private void drawMBO(GraphicsContext g, MapElement m) {
	//}
	
	//Highlights
	public void drawElementHighlight(GraphicsContext g) {
		int elementType = this.type - MapElement.HIGHLIGHT;

		if(elementType == MapElement.STATION || elementType == MapElement.TRAIN || elementType == MapElement.TRACKCONTROLLER)
			drawCircleHighlight(g);
		else if(elementType == MapElement.TRACK)
			drawLineHighlight(g);
		//else if(this.type == MapElement.MBOSEGMENT)
			//drawMBOHighlight(g);
		//else if(this.type == MapElement.YARD)
			//drawYardHighlight(g);
	}

	public void drawCircleHighlight(GraphicsContext g) {
		g.setFill(MapElement.color[MapElement.HIGHLIGHT]);
		g.fillOval(this.X.get(0) - HIGHLIGHT_WIDTH, this.Y.get(0) - HIGHLIGHT_WIDTH, 2*this.offset + 2*HIGHLIGHT_WIDTH, 2*this.offset + 2*HIGHLIGHT_WIDTH);
	}

	public void drawLineHighlight(GraphicsContext g) {
		g.setStroke(MapElement.color[MapElement.HIGHLIGHT]);
		g.setLineWidth(2*this.offset + 2*HIGHLIGHT_WIDTH);
		
		for(int i = 0; i < (this.X.size() - 1) && i < (this.Y.size() - 1); i++)
        	g.strokeLine(this.X.get(i), this.Y.get(i), this.X.get(i + 1), this.Y.get(i + 1));
	}
	
	//Collisions
	private boolean detectRectangle(double x, double y) {
		return ((y >= this.Y.get(0)) && (y <= (this.Y.get(0) + 6*this.offset)) && (x >= this.X.get(0)) && (x <= (this.X.get(0) + 8*this.offset)));
	}

	private boolean detectCircle(double x, double y) {
		return ((x - (this.X.get(0) + this.offset))*(x - (this.X.get(0) + this.offset)) + (y - (this.Y.get(0) + this.offset))*(y - (this.Y.get(0) + this.offset)) <= this.offset*this.offset);
	}

	private boolean detectLine(double x, double y) {
		double yint = this.Y.get(0) - this.X.get(0) * this.slope;
			if(this.slope == Double.POSITIVE_INFINITY || this.slope == Double.NEGATIVE_INFINITY)
				return (x >= (this.X.get(0) - this.offset) && x <= (this.X.get(0) + this.offset) && ((y <= this.Y.get(0)) && (y >= this.Y.get(1)) || ((y >= this.Y.get(0)) && (y <= this.Y.get(1)))));
		return (Math.abs(y - this.slope*x - yint)/Math.sqrt(slope*slope + 1) < this.offset) && ((x <= this.X.get(1) && x >= this.X.get(0)) || (x >= this.X.get(1) && x <= this.X.get(0)));
	}

	public boolean collision(double x, double y) {
		boolean result = false;
		
		if(this.type == MapElement.STATION)
			result = detectCircle(x, y);
		else if(this.type == MapElement.TRAIN)
			result = detectCircle(x, y);
		else if(this.type == MapElement.TRACK)
			result = detectLine(x, y);
		else if(this.type == MapElement.YARD)
			result = detectRectangle(x, y);
		
		return result;
	}
}